#import "PhotoKitPlugin.h"
#import "PhotoUtil.h"
#import "PhotoFrameUpdater.h"
#import "PhotoTextureController.h"

@interface PhotoKitPlugin ()<PHPhotoLibraryChangeObserver>

@property (readonly, weak, nonatomic) NSObject<FlutterTextureRegistry> *textureRegistry;
@property (nonatomic, strong) PHFetchResult<PHAsset *> *allPhotoAssets;
@property (nonatomic, strong) NSDictionary<NSString *, PHAsset *> *allPhotoAssetDic;
@property (nonatomic, strong, readonly) NSArray<NSDictionary *> *allPhotoInfos;
@property (nonatomic, strong) PHCachingImageManager *cacheManager;
@property (nonatomic, strong) NSMutableDictionary<NSString *, PhotoTextureController *> *textureControllerDic;

@end

@implementation PhotoKitPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
    FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"photo_kit" binaryMessenger:[registrar messenger]];
    PhotoKitPlugin *instance = [[PhotoKitPlugin alloc] initWithRegistrar:registrar];
    [registrar addMethodCallDelegate:instance channel:channel];
}

- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
    self = [super init];
    if (!self) return nil;
    _textureRegistry = registrar.textures;
    [self commonInit];
    
    return self;
}

- (void)commonInit {
    _textureControllerDic = NSMutableDictionary.dictionary;
    [PHPhotoLibrary.sharedPhotoLibrary registerChangeObserver:self];
    
    // 屏幕常亮
    UIApplication.sharedApplication.idleTimerDisabled = YES;
}

- (void)photoLibraryDidChange:(PHChange *)changeInstance {
    if (_allPhotoAssets) [self _rebuildAllPhotoAssets];
}

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
    if ([@"getPlatformVersion" isEqualToString:call.method]) {
        result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
    } else if ([@"getAllPhoto" isEqualToString:call.method]) {
        result(self.allPhotoInfos);
    } else if ([@"requestImageData" isEqualToString:call.method]) {
        NSDictionary *params = call.arguments;
        [self requestImageData:params[kIdentity] width:[params[kWidth] intValue] height:[params[kHeight] intValue] completion:^(NSData *imageData) {
            if (imageData) {
                result([FlutterStandardTypedData typedDataWithBytes:imageData]);
            }
            result(nil);
        }];
    } else if ([@"createTexture" isEqualToString:call.method]) {
        NSDictionary *params = call.arguments;
        result(@([self createTexture:params[kIdentity]]));
    } else if ([@"disposeTexture" isEqualToString:call.method]) {
        NSDictionary *params = call.arguments;
        [self disposeTexture:params[kIdentity]];
        result(nil);
    } else if ([@"updateTexture" isEqualToString:call.method]) {
        NSDictionary *params = call.arguments;
        [self updateTexture:params[kIdentity] width:[params[kWidth] intValue] height:[params[kHeight] intValue] completion:^(BOOL success) {
            result(@(success));
        }];
    } else {
        NSLog(@"%@ 方法找不到", call);
        result(FlutterMethodNotImplemented);
    }
}

#pragma mark - property

- (PHCachingImageManager *)cacheManager {
    if (!_cacheManager) {
        _cacheManager = [[PHCachingImageManager alloc] init];
    }
    return _cacheManager;
}

- (PHFetchResult<PHAsset *> *)allPhotoAssets {
    if (!_allPhotoAssets) {
        [self _rebuildAllPhotoAssets];
    }
    return _allPhotoAssets;
}

- (void)_rebuildAllPhotoAssets {
    PHFetchOptions *option = PHFetchOptions.new;
    PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:option];
    
    NSMutableDictionary *dic = NSMutableDictionary.dictionary;
    for (PHAsset *asset in result) {
        if (!asset.localIdentifier) return;
        
        dic[asset.localIdentifier] = asset;
    }
    
    _allPhotoAssets = result;
    _allPhotoAssetDic = dic.copy;
}

- (NSArray<NSDictionary *> *)allPhotoInfos {
    NSArray *photoInfos = ArrayMap((NSArray *)self.allPhotoAssets, ^id(PHAsset *item) {
        return [PhotoUtil dictionaryForPHAsset:item];
    });
    return photoInfos;
}


#pragma mark - core

- (void)checkAuthorizationWithCompletion:(void (^)(void))completion {
    switch (PHPhotoLibrary.authorizationStatus) {
        case PHAuthorizationStatusNotDetermined: {
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
                switch (status) {
                    case PHAuthorizationStatusAuthorized: {
                        if (completion) completion();
                    }
                        break;
                    default: {
                    }
                        break;
                }
            }];
        }
            break;
        case PHAuthorizationStatusAuthorized: {
            if (completion) completion();
        }
            break;
        default: {
        }
            break;
    }
}

- (void)requestOriginalImageData:(PHAsset *)asset completion:(void (^)(NSData *imageData))completion {
    PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init];
    option.networkAccessAllowed = YES;
    option.resizeMode = PHImageRequestOptionsResizeModeNone;
    option.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
    
    [PHImageManager.defaultManager requestImageDataForAsset:asset options:option resultHandler:^(NSData *imageData, NSString *_Nullable dataUTI, UIImageOrientation orientation, NSDictionary *_Nullable info) {
        BOOL isHEIF = [dataUTI isEqualToString:@"public.heif"] || [dataUTI isEqualToString:@"public.heic"];
        NSNumber *isICould = info[PHImageResultIsInCloudKey];
        isICould = isICould ?: @(isICould.boolValue);
        if (@available(iOS 10.0, *)) {
            if (isHEIF) {
                CIImage *ciImage = [CIImage imageWithData:imageData];
                CIContext *context = [CIContext context];
                imageData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{}];
                //NSMutableString *log = NSMutableString.string;
                //[log appendFormat:@"HEIF 结果 is in iCould: %@", isICould];
                //[log appendFormat:@", size: %@", NSStringFromCGSize(CGSizeMake(width, height))];
                //NSLog(@"%@", log);
            }
        }
        if (completion) completion(imageData);
    }];
}

- (void)requestImage:(PHAsset *)asset width:(int)width height:(int)height forThumb:(BOOL)forThumb completion:(void (^)(UIImage *image))completion {
    PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init];
    option.networkAccessAllowed = YES;
    option.resizeMode = PHImageRequestOptionsResizeModeExact;
    option.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
    
    PHImageManager *manager = PHImageManager.defaultManager;
    if (forThumb) manager = self.cacheManager;
    [manager requestImageForAsset:asset targetSize:CGSizeMake(width > 0 ? width : CGFLOAT_MAX, height > 0 ? height : CGFLOAT_MAX) contentMode:PHImageContentModeAspectFit options:option resultHandler:^(UIImage *_Nullable image, NSDictionary *_Nullable info) {
        if (completion) completion(image);
    }];
}

- (void)requestImageData:(NSString *)identity width:(int)width height:(int)height completion:(void (^)(NSData *imageData))completion {
    if (!identity) {
        if (completion) completion(nil);
        return;
    }
    PHAsset *asset = self.allPhotoAssetDic[identity];
    if (!asset) {
        if (completion) completion(nil);
        return;
    }
    
    if (width == -1 && height == -1) {
        [self requestOriginalImageData:asset completion:completion];
    } else {
        [self requestImage:asset width:width height:height forThumb:YES completion:^(UIImage *image) {
            if (!image) {
                if (completion) completion(nil);
                return;
            }
            
            NSData *imageData = UIImageJPEGRepresentation(image, 1);
            if (completion) completion(imageData);
        }];
    }
}

- (int64_t)createTexture:(NSString *)identity {
    PhotoFrameUpdater *frameUpdater = [[PhotoFrameUpdater alloc] initWithRegistry:_textureRegistry];
    PhotoTextureController *textureController = [[PhotoTextureController alloc] initWithAsset:_allPhotoAssetDic[identity] frameUpdater:frameUpdater];
    _textureControllerDic[identity] = textureController;
    
    int64_t textureId = [_textureRegistry registerTexture:textureController];
    frameUpdater.textureId = textureId;
    
    return textureId;
}

- (void)updateTexture:(NSString *)identity width:(int)width height:(int)height completion:(void (^)(BOOL success))completion {
    PhotoTextureController *textureController = _textureControllerDic[identity];
    if (!textureController) {
        NSLog(@"找到不 photo manager: %@", identity);
        return;
    }
    
    [textureController requestImage:self.cacheManager width:width height:height completion:completion];
}

- (void)disposeTexture:(NSString *)identity {
    PhotoTextureController *textureController = _textureControllerDic[identity];
    if (!textureController) {
        NSLog(@"找到不 photo manager: %@", identity);
        return;
    }
    [_textureRegistry unregisterTexture:textureController.frameUpdater.textureId];
    [textureController dispose];
    [_textureControllerDic removeObjectForKey:identity];
}

@end
